Skip to content

大規模ユーザー辞書向けのインデックス/キャッシュを追加#320

Draft
artin-kagun wants to merge 3 commits intoazooKey:mainfrom
artin-kagun:codex/user-dictionary-index-cache-only
Draft

大規模ユーザー辞書向けのインデックス/キャッシュを追加#320
artin-kagun wants to merge 3 commits intoazooKey:mainfrom
artin-kagun:codex/user-dictionary-index-cache-only

Conversation

@artin-kagun
Copy link
Copy Markdown

@artin-kagun artin-kagun commented May 5, 2026

目的

大きなユーザー辞書を使う場合に、変換時にすべての辞書項目を毎回動的辞書として組み立て直す負荷を減らします。

#318 で一度まとめて提案していた変更のうち、このPRでは「大規模ユーザー辞書を変換時に扱うためのcompiled dictionary利用」に範囲を絞っています。複数辞書管理、インポート / エクスポート、候補欄へのコメント表示は含めていません。

変更内容

  • 以前のcache/revision方式の実装をrevertしました。
  • ユーザー辞書とシステムユーザー辞書の内容からcompiled user dictionaryを生成する処理を追加しました。
  • ユーザー辞書の編集完了、削除、取り消し、システムユーザー辞書の読み込み/リセット時にcompiled dictionaryをbackgroundでexportするようにしました。
  • 初回起動時にcompiled dictionaryが存在しない場合のみ、現在の設定内容からexportするようにしました。
  • 変換時にはUserDefaults上のユーザー辞書全体を毎回dynamic dictionaryとして投入せず、compiled dictionaryを参照するようにしました。
  • compiled dictionaryに含められない読みについてはfallbackとして保持し、必要な読みだけdynamic dictionaryとして渡すようにしました。
  • compiled dictionaryの参照先が変換時に正しく使われることを確認するテストを追加しました。

意図

以前の実装では、変換のたびにユーザー辞書設定を読み出してdynamic dictionaryとして投入していたため、辞書が大きい場合に入力中の負荷が大きくなっていました。

今回の変更では、ユーザー辞書を設定したタイミングでexportし、変換時にはexport済みのcompiled dictionaryを利用する形に寄せています。これにより、変換中に重いexportや全件投入を行わずに済むようにしています。

候補数を上限で切るのではなく、辞書検索の入力経路をconverterが扱いやすい形式に寄せることで、変換候補の網羅性を落とさずに大規模辞書の負荷を下げる狙いです。

compiled dictionaryに含められない読みは捨てず、fallbackとして保持します。そのため、compiled dictionary対象外の項目も、入力中の読みと関係する場合は引き続き変換候補として出せます。

modificationDate は独自のrevision管理ではなく、実行中のIMEが設定変更後のcompiled dictionaryを再読み込みするための軽い更新検知として使っています。

確認したこと

  • 約40,000語規模のユーザー辞書を読み込んだ状態で、compiled dictionaryが作成されることをローカルで確認しました。
  • 約40,000語規模のユーザー辞書を有効にした状態で、登録語由来の候補が変換候補に出ることをローカルで確認しました。
  • テスト内で生成したcompiled dictionaryを変換時に参照し、登録した候補が返ることを確認しました。
  • compiled dictionaryの参照先が変換時に正しく使われることをテストで確認しました。
  • 候補数を上限で切るような処理は入れていません。

テスト

swift test --disable-sandbox --package-path Core --build-path /private/tmp/azookey-pr320-export-on-save-build --filter UserDictionary
swift test --disable-sandbox --package-path Core --build-path /private/tmp/azookey-pr320-export-on-save-build
git diff --check upstream/main
xcrun swiftc -parse azooKeyMac/AppDelegate.swift azooKeyMac/Windows/ConfigWindow.swift azooKeyMac/Windows/UserDictionaryEditorWindow.swift

@ensan-hcl
Copy link
Copy Markdown
Member

@artin-kagun
compile済みのuser dictionaryを利用するのは大筋で良いと思うのですが、revision管理周りが過剰に丁寧に実装されている感じがしてちょっと気になっています。iOS版のazooKeyではユーザ辞書の設定をいじったときにexport保存処理を実行して、その結果を変換側で使う設計で概ね問題なかったのですが、理由がなければもう少し単純な管理に寄せたいです。

@ensan-hcl
Copy link
Copy Markdown
Member

もう少し細かく実装を読んでみたのですが、今のrevision周りの実装はexportを変換のタイミングでやろうとしているのが原因だと思うので、ユーザ辞書を設定し終わったタイミングでexportするようにすることでもっとシンプルにできると思いました。

@artin-kagun
Copy link
Copy Markdown
Author

artin-kagun commented May 8, 2026

ご指摘ありがとうございます。

(※PRのコメントもEditしました)

revision管理が複雑になっていた点について、方針を見直しました。以前のcache/revision方式の実装はrevertし、ユーザー辞書の設定を変更したタイミングでcompiled dictionaryをexportし、変換時にはその結果を参照する方式として再実装しました。

force pushで履歴を書き換えるのではなく、既存実装のrevert commitと新方式のcommitを追加する形にしています。

実装としては、ユーザー辞書とシステムユーザー辞書の現在の設定内容から DicdataElement を作り、DictionaryBuilder.exportDictionary を使って Application Support/azooKey/memory/UserDictionary 以下にcompiled dictionaryを書き出すようにしています。変換時にはこのディレクトリをconverterのユーザー辞書URLとして渡します。

今回の変更では、ユーザー辞書編集画面での編集完了・削除・取り消し、およびシステムユーザー辞書の読み込み・リセット時にcompiled dictionaryをbackgroundでexportします。初回起動時には、compiled dictionaryがまだ存在しない場合のみ現在の設定内容からexportします。

変換時には、UserDefaults上のユーザー辞書全体を毎回読み出してdynamic dictionaryとして投入する処理をやめ、converterにcompiled dictionaryのURLを渡して参照させます。これにより、辞書が大きい場合でも変換中に全件を組み立て直す処理を避ける意図です。

一方で、compiled dictionaryに含められない読みもあります。そのため、compiled dictionaryに入れられない項目はfallbackとしてJSONに保存し、入力中の読みと関係するものだけdynamic dictionaryとして渡すようにしています。ここでは候補数を上限で切るような処理は入れていないため、変換精度を落とす方向の対応にはしていません。

実行中のIMEが設定変更後のcompiled dictionaryを再読み込みできるように、metadata.jsonmodificationDate を軽い更新検知として使っています。これは独自のrevision管理ではなく、export済みファイルが更新されたかを見てconverterにreloadを伝えるためのものです。

また、converter側では requestCandidates の options に含まれる sharedContainerURL によってユーザー辞書URLが更新されるため、updateUserDictionaryURL だけでなく sharedContainerURL もcompiled dictionaryのディレクトリを指すようにしています。この点については、テスト内で作成したcompiled dictionaryを変換時に参照し、登録した候補が返ることを確認するテストを追加しました。

ローカルでは、約40,000語規模のユーザー辞書を読み込んだ状態でcompiled dictionaryが作成され、登録語由来の候補が変換候補に出ることを確認しています。

確認したコマンドは以下です。

swift test --disable-sandbox --package-path Core --build-path /private/tmp/azookey-pr320-export-on-save-build --filter UserDictionary
swift test --disable-sandbox --package-path Core --build-path /private/tmp/azookey-pr320-export-on-save-build
git diff --check upstream/main
xcrun swiftc -parse azooKeyMac/AppDelegate.swift azooKeyMac/Windows/ConfigWindow.swift azooKeyMac/Windows/UserDictionaryEditorWindow.swift

関連して、#319 ではユーザー辞書のインポート/エクスポート機能も提案していますが、大規模な辞書をインポートできるようにする場合、複数の辞書を分けて管理できると、分野ごとの有効/無効切り替えや辞書単位での整理がしやすくなると考えています。
(そうだとすると、大規模ユーザー辞書向けのこのPRは必要なのかという論点も出てきますね)
一方で、azooKeyのユーザー辞書を小規模な手動追加を主な用途として扱う方針であれば、現在の単一ユーザー辞書のままでも十分だと思います。

@ensan-hcl
Copy link
Copy Markdown
Member

一方で、compiled dictionaryに含められない読みもあります。そのため、compiled dictionaryに入れられない項目はfallbackとしてJSONに保存し、入力中の読みと関係するものだけdynamic dictionaryとして渡すようにしています。ここでは候補数を上限で切るような処理は入れていないため、変換精度を落とす方向の対応にはしていません。

おそらく辞書データに利用できないreadingの存在の話かと思います。確かに、数字・英字・カタカナくらいしかサポートしていないはずです。それ以外を読みとする単語はそもそも入力やimport(別PRですが)のvalidationで弾くようにして、fallbackにJSONを使うのは避けたいです。

実行中のIMEが設定変更後のcompiled dictionaryを再読み込みできるように、metadata.json の modificationDate を軽い更新検知として使っています。これは独自のrevision管理ではなく、export済みファイルが更新されたかを見てconverterにreloadを伝えるためのものです。

確かKanaKanjiConverterインスタンスはIMEアプリケーション全体でシングルトンに近い扱いになっていると思うので、単にそれを経由してuser dictionaryのreloadをトリガーするのがよりシンプルな気がしました。metadata.jsonを不要にできると思います。

一般に、ユーザ環境によく設計されていないファイルを保存していくのは将来的に負債になるので、避けたいです。

@ensan-hcl
Copy link
Copy Markdown
Member

あと、Ubuntu向けのTest / Buildが通っていないので修正お願いします! 🙏

@artin-kagun
Copy link
Copy Markdown
Author

ご指摘ありがとうございます。

ご指摘いただいた内容に合わせて、fallback JSON と metadata.json を使わない形に整理した修正版を別PRとして出させていただこうと思います。

具体的には、compiled dictionary に含められない読みは fallback JSON に保存せず export 対象から除外し、変換時に fallback JSON を dynamic dictionary として渡す処理も削除しました。また、metadata.json の modificationDate による更新検知はやめ、ユーザー辞書の保存後に共有の KanaKanjiConverter インスタンスへ明示的に reload を通知する形に変更しています。

既存の手動ユーザー辞書UIには読みのvalidationがないため、このPRではUI仕様変更までは行わず、入力時・import時のvalidationについては import 機能側のPRで扱う想定です。

Ubuntu CIについては、主要な compiled dictionary export テストがLinux環境でも実行できるよう、テスト用の小さな charID.chid を注入できる形に修正しました。ただし最終的にはGitHub Actions上で確認する必要があるため、一度PRを出し、もし失敗した場合は追加修正します。

お手数をおかけしました。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants